﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Configuration;

using Tao.FreeGlut;
using Tao.OpenGl;

namespace tp3
{
    class Program
    {
        static int Main(string[] args)
        {
            Main main = new Main();
            main.Inicializar(args);
            return 0;
        }
    }

    class Main
    {
        // Variables que controlan la ubicación de la cámara en la Escena 3D

        private float[] _eye = new float[] { 15f, 15f, 5f };
        private float[] _at = new float[] { 0f, 0f, 0f };
        private float[] _up = new float[] { 0f, 0f, 1f };

        // Variables asociadas a única fuente de luz de la escena
        bool dia = true;

        private float[] _lightColor = new float[] { 1f, 1f, 1f, 1f };
        private float[] _lightAmbient = new float[] { 0.5f, 0.5f, 0.5f };
        private float[] _lightPosition = new float[] { 0.05f, 0.05f, 0.05f, 1.0f };

        private float[] _lightColorSpot = new float[] { 1f, 1f, 1f, 0.1f };

        // Color de la esfera en movimiento dentro de la escena
        private float[] _colorEsfera = new float[] { 0.5f, 0.5f, 0.2f, 1.0f };

        // Variable asociada al movimiento de rotación de la esfera alrededor del eje Z
        private float _rotateShpere = 0;

        // Variables de control
        private bool _viewGrid = true;
        private bool _viewAxis = true;



        // Handle para el control de las Display Lists
        private int _dlHandle;

        public int DlAxis { get { return _dlHandle; } }
        public int DlGrid { get { return _dlHandle + 1; } }
        public int DlAxis2DTop { get { return _dlHandle + 2; } }
        public int DlAxis2DHeight { get { return _dlHandle + 3; } }


        // Tamaño de la ventana
        private float[] _windowSize = new float[2];
        public float WindowWidth
        {
            get { return _windowSize[0]; }
            set { _windowSize[0] = value; }
        }
        public float WindowHeight
        {
            get { return _windowSize[1]; }
            set { _windowSize[1] = value; }
        }

        public int TopViewPosX { get { return (int)(WindowWidth * 0.7); } }
        public int TopViewWidth { get { return (int)(WindowWidth * 0.3); } }
        public int TopViewPosY { get { return (int)(WindowHeight * 0.7); } }
        public int TopViewHeight { get { return (int)(WindowHeight * 0.3); } }

        public int HeightViewPosX { get { return (int)(WindowWidth * 0.7); } }
        public int HeightViewWidth { get { return (int)(WindowWidth * 0.3); } }
        public int HeightViewPosY { get { return (int)(WindowHeight * 0.4); } }
        public int HeightViewHeight { get { return (int)(WindowHeight * 0.3); } }


        // Global variables
        string caption = "Sistema Gráficos - 66.71 - 2008c2";

        private bool dibujarCurva = false;
        private TipoTramo _tipoTramo = TipoTramo.Normal;

        private PuntoDeControl puntoAlturaClickeado;
        private PuntoDeControl puntoVistaSuperiorClickeado;

        //módulo para las tangentes
        private float MODULO = float.Parse(ConfigurationManager.AppSettings["ModuloTangentes"]);

        //para la rotación 3D
        private int xAnterior;
        private int yAnterior;


        //en radianes
        public float[] cartesianasAEsfericas(float[] cartesianas)
        {
            float[] esfericas = new float[3];
            esfericas[0] = getRho(cartesianas[0], cartesianas[1], cartesianas[2]); //rho
            esfericas[1] = (float)Math.Atan2(cartesianas[1], esfericas[0]); //phi
            esfericas[2] = (float)Math.Acos(cartesianas[2] / esfericas[0]); //theta

            return esfericas;
        }

        public float getRho(float x, float y, float z)
        {
            return (float)Math.Sqrt(Math.Pow(x, 2) + Math.Pow(y, 2) + Math.Pow(z, 2));
        }

        //en radianes
        public float[] esfericasACartesianas(float[] esfericas)
        {
            float sinPhi = (float)Math.Sin(esfericas[1]);
            float cosPhi = (float)Math.Cos(esfericas[1]);
            float sinTheta = (float)Math.Sin(esfericas[2]);
            float cosTheta = (float)Math.Cos(esfericas[2]);
            float rho = esfericas[0];

            float[] cartesianas = new float[3];
            cartesianas[0] = rho * sinTheta * cosPhi;
            cartesianas[1] = rho * sinPhi * sinTheta;
            cartesianas[2] = rho * cosTheta;

            return cartesianas;
        }

        private void reshape(int w, int h)
        {
            WindowWidth = w;
            WindowHeight = h;
        }


        private void init()
        {
            Gl.glEnable(Gl.GL_NORMALIZE);
            _dlHandle = Gl.glGenLists(3);
            Gl.glClearColor(0.02f, 0.02f, 0.04f, 0.0f);
            Gl.glShadeModel(Gl.GL_SMOOTH);
            Gl.glEnable(Gl.GL_DEPTH_TEST);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_SPECULAR, _lightColor);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, _lightColor);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, _lightAmbient);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, _lightPosition);
            Gl.glEnable(Gl.GL_LIGHT0);

            Gl.glLightfv(Gl.GL_LIGHT2, Gl.GL_SPECULAR, _lightColorSpot);
            Gl.glLightfv(Gl.GL_LIGHT2, Gl.GL_DIFFUSE, _lightColorSpot);

            Gl.glLightf(Gl.GL_LIGHT2, Gl.GL_SPOT_EXPONENT, 100);
            Gl.glLightf(Gl.GL_LIGHT2, Gl.GL_SPOT_CUTOFF, 91);

            //Gl.glEnable(Gl.GL_LIGHT2);

            Gl.glEnable(Gl.GL_LIGHTING);

            // Generación de las Display Lists
            Gl.glNewList(DlAxis, Gl.GL_COMPILE);
            DrawAxis();
            Gl.glEndList();
            Gl.glNewList(DlGrid, Gl.GL_COMPILE);
            DrawXYGrid();
            Gl.glEndList();
            Gl.glNewList(DlAxis2DTop, Gl.GL_COMPILE);
            DrawAxis2DTopView();
            Gl.glEndList();
            Gl.glNewList(DlAxis2DHeight, Gl.GL_COMPILE);
            DrawAxis2DHeightView();
            Gl.glEndList();
        }

        List<PuntoDeControl> puntosDeControlVistaSuperior = new List<PuntoDeControl>();
        List<PuntoDeControl> puntosDeControlAlturas = new List<PuntoDeControl>();

        private void display()
        {
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
            ///////////////////////////////////////////////////
            // Escena 3D
            Set3DEnv();

            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();
            Glu.gluLookAt(_eye[0], _eye[1], _eye[2], _at[0], _at[1], _at[2], _up[0], _up[1], _up[2]);
            Gl.glRotatef(_rotateShpere, 0, 0, 1);
            if (_viewAxis)
            {
                Gl.glCallList(DlAxis);
            }

            if (_viewGrid)
            {
                Gl.glCallList(DlGrid);
            }

            Curva.BSplineCubica3D(puntosDeControlVistaSuperior);
            if (dibujarCurva)
            {
                if (puntosDeControlAlturas.Count == 0 || puntosDeControlVistaSuperior.Count == 0)
                {
                    dibujarCurva = false;
                }
                else
                {
                    Escena.Dibujar(puntosDeControlVistaSuperior, puntosDeControlAlturas);
                }
            }

            //
            ///////////////////////////////////////////////////


            ///////////////////////////////////////////////////
            // Panel 2D para la vista superior
            SetPanelTopEnv();
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();
            Glu.gluLookAt(0, 0, 0.5, 0, 0, 0, 0, 1, 0);
            Gl.glCallList(DlAxis2DTop);
            Curva.BSplineCubica(puntosDeControlVistaSuperior);
            //
            ///////////////////////////////////////////////////


            ///////////////////////////////////////////////////
            // Panel 2D para la vista del perfil de altura
            SetPanelHeightEnv();
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();
            Glu.gluLookAt(0, 0, 0.5, 0, 0, 0, 0, 1, 0);
            Gl.glCallList(DlAxis2DHeight);
            Curva.BezierCubica(puntosDeControlAlturas);
            //
            ///////////////////////////////////////////////////
            Glut.glutSwapBuffers();
        }


        private void keyboard(byte key, int x, int y)
        {
            switch (key)
            {
                case 27://esc
                case (byte)'x':
                case (byte)'X':
                    System.Environment.Exit(1);
                    break;
                case (byte)'G': //P
                case (byte)'g': //p
                    _viewGrid = !_viewGrid;
                    Glut.glutPostRedisplay();
                    break;
                case (byte)'A': //A
                case (byte)'a': //a
                    _viewAxis = !_viewAxis;
                    Glut.glutPostRedisplay();
                    break;
                case (byte)'B':
                case (byte)'b':
                    puntosDeControlAlturas.Clear();
                    puntosDeControlVistaSuperior.Clear();
                    dibujarCurva = false;
                    Glut.glutPostRedisplay();
                    break;
                case (byte)'c':
                case (byte)'C':
                    dibujarCurva = true;
                    Glut.glutPostRedisplay();
                    break;
                case (byte)'n':
                case (byte)'N':
                    rho -= 0.5f;
                    actualizarEye(thetaAnterior, phiAnterior);
                    Glut.glutPostRedisplay();
                    break;
                case (byte)'m':
                case (byte)'M':
                    rho += 0.5f;
                    actualizarEye(thetaAnterior, phiAnterior);
                    Glut.glutPostRedisplay();
                    break;
                case (byte)'1':
                    _tipoTramo = TipoTramo.Normal;
                    break;
                case (byte)'2':
                    _tipoTramo = TipoTramo.Cilindro;
                    break;
                case (byte)'3':
                    _tipoTramo = TipoTramo.BaseRectangular;
                    break;
                case (byte)'+':
                    if (Escena.AvanceCarrito < 50)
                    {
                        Escena.AvanceCarrito += 2;
                    }
                    break;
                case (byte)'-':
                    if (Escena.AvanceCarrito > 0)
                    {
                        Escena.AvanceCarrito -= 2;
                    }
                    break;
                case (byte)'v':
                    camaraCarrito = !camaraCarrito;
                    if (!camaraCarrito)
                    {
                        actualizarEye(thetaAnterior, phiAnterior);
                        _at = new float[] { 0f, 0f, 0f };
                        _up = new float[] { 0f, 0f, 1f };
                    }
                    break;
                case (byte)'i':
                case (byte)'I':
                    dia = !dia;
                    if (dia)
                    {
                        _lightColor = new float[] { 1f, 1f, 1f, 1f };
                        _lightAmbient = new float[] { 0.5f, 0.5f, 0.5f };
                        Gl.glDisable(Gl.GL_LIGHT2);
                    }
                    else
                    {
                        _lightColor = new float[] { 0f, 0f, 0.3f, 0.9f };
                        _lightAmbient = new float[] { 0f, 0f, 0.3f };
                        Gl.glEnable(Gl.GL_LIGHT2);
                    }
                    Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_SPECULAR, _lightColor);
                    Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, _lightColor);
                    Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, _lightAmbient);
                    Glut.glutPostRedisplay();
                    break;
                default:
                    break;
            }
        }

        private void arrowKeys(int key, int x, int y)
        {
            switch (key)
            {
                case 100: //izquierda
                    break;
                case 101: //arriba
                    break;
                case 102: //derecha
                    break;
                case 103: //abajo
                    break;
            }
        }


        private void mouseClick(int button, int state, int x, int y)
        {
            //ver en qué viewport cae el click
            y = (int)WindowHeight - y;
            if (x >= HeightViewPosX && x <= HeightViewPosX + HeightViewWidth &&
                y >= HeightViewPosY && y <= HeightViewPosY + HeightViewHeight)
            {
                viewportAlturasClick(button, state, x, y);
            }
            else if (x >= TopViewPosX && x <= TopViewPosX + TopViewWidth &&
                y >= TopViewPosY && y <= TopViewPosY + TopViewHeight)
            {
                viewportVistaSuperiorClick(button, state, x, y);
            }
            else
            {
                xAnterior = x;
                yAnterior = y;
            }
        }

        private void viewportAlturasClick(int button, int state, int x, int y)
        {
            float xMundo = (x - HeightViewPosX) / (float)HeightViewWidth;
            float yMundo = (y - HeightViewPosY) / (float)HeightViewHeight;
            if (button == Glut.GLUT_RIGHT_BUTTON && state == Glut.GLUT_DOWN)
            {
                agregarPuntoDeControlAltura(xMundo, yMundo);
            }
            else if (button == Glut.GLUT_LEFT_BUTTON)
            {
                //muevo un punto
                if (state == Glut.GLUT_DOWN)
                {
                    foreach (PuntoDeControl punto in puntosDeControlAlturas)
                    {
                        if (punto.HitTest(xMundo, yMundo))
                        {
                            puntoAlturaClickeado = punto;
                        }
                    }
                }
                else
                {
                    puntoAlturaClickeado = null;
                }
            }
            else if (button == Glut.GLUT_MIDDLE_BUTTON)
            {
                int puntoBorrado = -1;
                for (int i = 0; i < puntosDeControlAlturas.Count; i++)
                {
                    if (puntosDeControlAlturas[i].HitTest(xMundo, yMundo))
                    {
                        puntoBorrado = i;
                    }
                }
                if (puntoBorrado >= 0 && (puntoBorrado % 3 == 0 || puntoBorrado % 3 == 3) && puntosDeControlAlturas.Count > 1)
                {
                    if (puntoBorrado == 0)
                    {
                        //es el primer punto, borro a él y a los 2 siguientes
                        puntosDeControlAlturas.RemoveRange(0, 3);
                    }
                    else if (puntoBorrado == puntosDeControlAlturas.Count - 1)
                    {
                        //es el último punto, borro a él y a los 2 anteriores
                        puntosDeControlAlturas.RemoveRange(puntosDeControlAlturas.Count - 3, 3);
                    }
                    else
                    {
                        //es un punto intermedio, borro a él, al anterior y al siguiente
                        puntosDeControlAlturas.RemoveRange(puntoBorrado - 1, 3);
                    }
                }
            }
        }

        private void viewportVistaSuperiorClick(int button, int state, int x, int y)
        {
            float xMundo = (x - TopViewPosX) / (float)TopViewWidth;
            float yMundo = (y - TopViewPosY) / (float)TopViewHeight;
            if (button == Glut.GLUT_RIGHT_BUTTON && state == Glut.GLUT_DOWN)
            {
                puntosDeControlVistaSuperior.Add(new PuntoDeControl(xMundo, yMundo, _tipoTramo));
            }
            else if (button == Glut.GLUT_LEFT_BUTTON)
            {
                //muevo un punto
                if (state == Glut.GLUT_DOWN)
                {
                    foreach (PuntoDeControl punto in puntosDeControlVistaSuperior)
                    {
                        if (punto.HitTest(xMundo, yMundo))
                        {
                            puntoVistaSuperiorClickeado = punto;
                        }
                    }
                }
                else
                {
                    puntoVistaSuperiorClickeado = null;
                }
            }
            else if (button == Glut.GLUT_MIDDLE_BUTTON)
            {
                PuntoDeControl puntoBorrado = null;
                foreach (PuntoDeControl punto in puntosDeControlVistaSuperior)
                {
                    if (punto.HitTest(xMundo, yMundo))
                    {
                        puntoBorrado = punto;
                    }
                }
                if (puntoBorrado != null)
                {
                    puntosDeControlVistaSuperior.Remove(puntoBorrado);
                }
            }
        }



        private void agregarPuntoDeControlAltura(float xMundo, float yMundo)
        {
            if (puntosDeControlAlturas.Count == 0)
            {
                puntosDeControlAlturas.Add(new PuntoDeControl(xMundo, yMundo));
            }
            else if (xMundo > puntosDeControlAlturas[puntosDeControlAlturas.Count - 1].X)
            {

                PuntoDeControl nuevoPuntoDeControl = new PuntoDeControl(xMundo, puntosDeControlAlturas[0].Y);
                //primer punto de tangente
                puntosDeControlAlturas.Add(new PuntoDeControl(puntosDeControlAlturas[puntosDeControlAlturas.Count - 1].X + MODULO, puntosDeControlAlturas[puntosDeControlAlturas.Count - 1].Y));
                //segundo punto de tangente
                puntosDeControlAlturas.Add(new PuntoDeControl(nuevoPuntoDeControl.X - MODULO, nuevoPuntoDeControl.Y));
                //punto final
                puntosDeControlAlturas.Add(nuevoPuntoDeControl);

                //actualizo las tangentes
                actualizarTangentes();
            }
        }

        private void actualizarTangentes()
        {
            for (int i = 3; i < puntosDeControlAlturas.Count; i += 3)
            {
                if (i == puntosDeControlAlturas.Count - 1)
                {
                    //es el último punto de control
                    //calculo sólo el anterior (pongo true como para calcular el "siguiente", porque la resta del siguiente y el anterior queda invertida por los vectores
                    puntosDeControlAlturas[1] = obtenerPuntoTangente(new PuntoDeControl(1, 0), puntosDeControlAlturas[0], new PuntoDeControl(0, 0), false);
                    puntosDeControlAlturas[puntosDeControlAlturas.Count - 2] = obtenerPuntoTangente(new PuntoDeControl(1, 0), puntosDeControlAlturas[i], new PuntoDeControl(0, 0), true);
                }
                else
                {
                    //es un punto intermedio
                    //calculo el nuevo Y para el punto tangente anterior y siguiente
                    puntosDeControlAlturas[i - 1] = obtenerPuntoTangente(puntosDeControlAlturas[i - 3], puntosDeControlAlturas[i], puntosDeControlAlturas[i + 3], false);
                    puntosDeControlAlturas[i + 1] = obtenerPuntoTangente(puntosDeControlAlturas[i - 3], puntosDeControlAlturas[i], puntosDeControlAlturas[i + 3], true);
                }
            }
        }

        private PuntoDeControl obtenerPuntoTangente(PuntoDeControl anterior, PuntoDeControl actual, PuntoDeControl siguiente, bool posterior)
        {
            float lambda = (posterior) ? MODULO : -MODULO;
            return lambda * (siguiente - anterior) + actual;
        }


        private void mouseMotion(int x, int y)
        {
            y = (int)WindowHeight - y;

            if (puntoAlturaClickeado != null)
            {
                if (x > HeightViewPosX && x < HeightViewPosX + HeightViewWidth && y > HeightViewPosY && y < HeightViewPosY + HeightViewHeight)
                {
                    float xMundo = (x - HeightViewPosX) / (float)HeightViewWidth;
                    float yMundo = (y - HeightViewPosY) / (float)HeightViewHeight;
                    int index = puntosDeControlAlturas.IndexOf(puntoAlturaClickeado);
                    if ((index % 3 == 0 || index % 3 == 3) &&
                        ((index == 0 && puntosDeControlAlturas.Count == 1) ||
                        (index == 0 && xMundo < puntosDeControlAlturas[3].X) ||
                        (index == puntosDeControlAlturas.Count - 1 && xMundo > puntosDeControlAlturas[index - 3].X) ||
                        (index != 0 && index != puntosDeControlAlturas.Count - 1 && xMundo > puntosDeControlAlturas[index - 3].X && xMundo < puntosDeControlAlturas[index + 3].X)))
                    {
                        puntoAlturaClickeado.X = xMundo;
                    }
                    puntoAlturaClickeado.Y = yMundo;
                    if (puntoAlturaClickeado == puntosDeControlAlturas[0])
                    {
                        puntosDeControlAlturas[puntosDeControlAlturas.Count - 1].Y = yMundo;
                    }
                    else if (puntoAlturaClickeado == puntosDeControlAlturas[puntosDeControlAlturas.Count - 1])
                    {
                        puntosDeControlAlturas[0].Y = yMundo;
                    }

                    if (puntosDeControlAlturas.Count > 1)
                    {
                        actualizarTangentes();
                    }
                    Glut.glutPostRedisplay();
                }
            }
            else if (puntoVistaSuperiorClickeado != null)
            {
                if (x < TopViewPosX + TopViewWidth && x > TopViewPosX && y > TopViewPosY)
                {
                    float xMundo = (x - TopViewPosX) / (float)TopViewWidth;
                    float yMundo = (y - TopViewPosY) / (float)TopViewHeight;
                    puntoVistaSuperiorClickeado.X = xMundo;
                    puntoVistaSuperiorClickeado.Y = yMundo;
                    Glut.glutPostRedisplay();
                }
            }
            else if (x < HeightViewPosX || x > HeightViewWidth && y < HeightViewPosY)
            {
                //rotacion esférica
                float deg2rad = (float)(Math.PI / 180);

                float theta = thetaAnterior + (float)((2 * Math.PI / WindowHeight * (y - yAnterior) * deg2rad * 20) % Math.PI);
                float phi = phiAnterior + (float)((2 * Math.PI / WindowWidth * (x - xAnterior) * deg2rad * 20) % (2 * Math.PI));

                actualizarEye(theta, phi);

                thetaAnterior = theta;
                phiAnterior = phi;
                xAnterior = x;
                yAnterior = y;
                Glut.glutPostRedisplay();
            }
        }

        private void actualizarEye(float theta, float phi)
        {
            _eye[0] = (float)(rho * Math.Sin(theta) * Math.Cos(-phi));
            _eye[1] = (float)(rho * Math.Sin(theta) * Math.Sin(-phi));
            _eye[2] = (float)(rho * Math.Cos(theta));
        }
        private float thetaAnterior = 1.34f;
        private float phiAnterior = -0.54f;
        private float rho = 22f;


        bool camaraCarrito = false;
        private void timer(int time)
        {
            if (dibujarCurva && camaraCarrito)
            {
                Punto eye = 20f * Escena.DireccionCarrito + Escena.PuntoCarrito + new Punto(0, 0, 1);
                _eye = new float[] { (float)eye.X, (float)eye.Y, (float)eye.Z };
                Punto at = 150 * Escena.DireccionCarrito + Escena.PuntoCarrito;
                _at = new float[] { (float)at.X, (float)at.Y, (float)at.Z };
                _up = new float[] { 0f, 0f, 1f };


                //Punto at = 
            }
            Glut.glutPostRedisplay();

            Glut.glutTimerFunc(150, new Glut.TimerCallback(timer), 0);
        }

        public int Inicializar(string[] args)
        {
            StringBuilder[] sb = new StringBuilder[args.Length];
            int i = 0;
            foreach (String s in args)
            {
                sb.SetValue(args[i], i);
                i++;
            }

            int argc = args.Length;

            Glut.glutInit(ref argc, sb);
            Glut.glutInitDisplayMode(Glut.GLUT_DOUBLE | Glut.GLUT_RGB | Glut.GLUT_DEPTH);
            Glut.glutInitWindowSize(1024, 768);
            Glut.glutInitWindowPosition(0, 0);

            Glut.glutCreateWindow(caption);
            Glut.glutFullScreen();
            init();

            Glut.glutKeyboardFunc(new Glut.KeyboardCallback(keyboard));
            Glut.glutSpecialFunc(new Glut.SpecialCallback(arrowKeys));
            Glut.glutDisplayFunc(new Glut.DisplayCallback(display));
            Glut.glutReshapeFunc(new Glut.ReshapeCallback(reshape));
            Glut.glutTimerFunc(50, new Glut.TimerCallback(timer), 0);

            Glut.glutMouseFunc(new Glut.MouseCallback(mouseClick));
            Glut.glutMotionFunc(new Glut.MotionCallback(mouseMotion));
            Glut.glutMainLoop();
            return 0;
        }

        public void DrawAxis()
        {
            Gl.glDisable(Gl.GL_LIGHTING);
            Gl.glBegin(Gl.GL_LINES);
            // X
            Gl.glColor3f(1f, 0f, 0f);
            Gl.glVertex3f(0f, 0f, 0f);
            Gl.glColor3f(0f, 0f, 0f);
            Gl.glVertex3f(15f, 0f, 0f);
            // Y
            Gl.glColor3f(0f, 1f, 0f);
            Gl.glVertex3f(0f, 0f, 0f);
            Gl.glColor3f(0f, 0f, 0f);
            Gl.glVertex3f(0f, 15f, 0f);
            // Z
            Gl.glColor3f(0f, 0f, 1f);
            Gl.glVertex3f(0f, 0f, 0f);
            Gl.glColor3f(0f, 0f, 0f);
            Gl.glVertex3f(0f, 0f, 15f);
            Gl.glEnd();
            Gl.glEnable(Gl.GL_LIGHTING);
        }

        public void DrawAxis2DTopView()
        {
            Gl.glDisable(Gl.GL_LIGHTING);
            Gl.glBegin(Gl.GL_LINES);
            // X
            Gl.glColor3f(1f, 0f, 0f);
            Gl.glVertex3f(0f, 0f, 0f);
            Gl.glVertex3f(1f, 0f, 0f);
            // Y
            Gl.glColor3f(0f, 1f, 0f);
            Gl.glVertex3f(0f, 0f, 0f);
            Gl.glVertex3f(0f, 1f, 0f);
            Gl.glEnd();
            Gl.glEnable(Gl.GL_LIGHTING);
        }

        public void DrawAxis2DHeightView()
        {
            Gl.glDisable(Gl.GL_LIGHTING);
            Gl.glBegin(Gl.GL_LINES);
            // U
            Gl.glColor3f(0.5f, 0.5f, 0f);
            Gl.glVertex3f(0f, 0f, 0f);
            Gl.glVertex3f(1f, 0f, 0f);
            // Z
            Gl.glColor3f(0f, 0f, 1f);
            Gl.glVertex3f(0f, 0f, 0f);
            Gl.glVertex3f(0f, 1f, 0f);
            Gl.glEnd();
            Gl.glEnable(Gl.GL_LIGHTING);
        }

        public void DrawXYGrid()
        {
            int i;
            Gl.glDisable(Gl.GL_LIGHTING);
            Gl.glColor3f(0.15f, 0.1f, 0.1f);
            Gl.glBegin(Gl.GL_LINES);
            for (i = -20; i < 21; i++)
            {
                Gl.glVertex3f(i, -20f, 0f);
                Gl.glVertex3f(i, 20f, 0f);
                Gl.glVertex3f(-20f, i, 0f);
                Gl.glVertex3f(20f, i, 0f);
            }
            Gl.glEnd();
            Gl.glEnable(Gl.GL_LIGHTING);
        }

        public void Set3DEnv()
        {
            Gl.glViewport(0, 0, (int)WindowWidth, (int)WindowHeight);
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            Glu.gluPerspective(60, WindowWidth / WindowHeight, 0.10, 100.0);
        }

        public void SetPanelTopEnv()
        {
            Gl.glViewport(TopViewPosX, TopViewPosY, TopViewWidth, TopViewHeight);
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            Glu.gluOrtho2D(0, 1, 0, 1);
        }

        public void SetPanelHeightEnv()
        {
            Gl.glViewport(HeightViewPosX, HeightViewPosY, HeightViewWidth, HeightViewHeight);
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            Glu.gluOrtho2D(0, 1, 0, 1);
        }
    }
}
